﻿using WH.Model.OrgContext;
using Rw.Core.Common.Paging;
using Rw.Core.ORM.Domain;
using WH.Common.Bases.IBase;
using WH.Common.Paging;
using SqlSugar;
using System.Linq.Expressions;
using System.Reflection;
using IAuditable = WH.Common.Bases.IBase.IAuditable;
using WH.Model.Bases;

namespace WH.Repositories.BaseRepositories
{
    /// <summary>
    /// 基础仓储类
    /// </summary>
    /// <typeparam name="TAggregateRoot"></typeparam>
    public abstract class BaseRepository<TAggregateRoot> : IBaseRepository<TAggregateRoot> where TAggregateRoot : class, IAggregateRoot<string>, new()
    {
        private bool _isOrgFilter = true;

        protected readonly ISqlSugarClient DbContext;

        //protected virtual ISugarQueryable<TAggregateRoot> Query;
        protected virtual ISugarQueryable<TAggregateRoot> Query
        {
            get
            {
                var query = DbContext.Queryable<TAggregateRoot>();

                var t = typeof(TAggregateRoot);

                if (t.GetInterface(nameof(IOrganizationFilter)) is not null)
                {
                    // 获取实现类中相同名称的属性信息
                    var property = t.GetProperty(nameof(IOrganizationFilter.OrganizationId));
                    if (property is not null)
                    {
                        var attr = property.GetCustomAttribute<SugarColumn>();

                        if (attr is not null)
                        {
                            var colName = string.IsNullOrWhiteSpace(attr.ColumnName) ? nameof(IOrganizationFilter.OrganizationId) : attr.ColumnName;

                            if (_orgContext.OrganizationIds is not null)
                            {
                                query.InIF(_isOrgFilter, $"{colName}", _orgContext.OrganizationIds);
                            }
                        }
                    }
                }

                return query;
            }
        }

        protected readonly OrganizationContext _orgContext;

        public BaseRepository(ISqlSugarClient dbContext, OrganizationContext organizationContext)
        {
            DbContext = dbContext;

            _orgContext = organizationContext;

            //Query = DbContext.CopyNew().Queryable<TAggregateRoot>(); 
        }

        public virtual IBaseRepository<TAggregateRoot> NoFilter()
        {
            //DbContext.QueryFilter.Clear();

            //_orgContext.OrganizationIds = null;

            _isOrgFilter = false;

            return this;
        }

        public virtual async Task<bool> AnyAsync()
        {
            return await Query.AnyAsync();
        }

        public virtual async Task<bool> AnyAsync(Expression<Func<TAggregateRoot, bool>> predicate)
        {
            return await Query.Where(predicate).AnyAsync();
        }

        public virtual async Task<int> CountAsync(Expression<Func<TAggregateRoot, bool>>? predicate = null)
        {
            return await Query.WhereIF(predicate is not null, predicate).CountAsync();
        }

        public virtual async Task<int> CreateAsync(TAggregateRoot entity)
        {
            SetCreatedInfo(entity);
            return await DbContext.Insertable(entity).ExecuteCommandAsync();
        }

        public virtual async Task<int> CreateAsync(IEnumerable<TAggregateRoot> entities)
        {
            var data = entities.ToList();

            foreach (var entity in data)
            {
                SetCreatedInfo(entity);
            }

            return await DbContext.Insertable(data).ExecuteCommandAsync();
        }

        public virtual void Dispose()
        {
            DbContext.Dispose();
        }

        public virtual async Task<bool> ExistsAsync(string key)
        {
            return await Query.AnyAsync(x => x.Id == key);
        }

        public virtual async Task<bool> ExistsAsync(Expression<Func<TAggregateRoot, bool>> predicate)
        {
            return await Query.AnyAsync(predicate);
        }

        public virtual async Task<IEnumerable<TAggregateRoot>> FindAllAsync(Expression<Func<TAggregateRoot, bool>>? predicate = null, bool withCache = false)
        {
            var list = await Query.WhereIF(predicate is not null, predicate).WithCacheIF(withCache).ToListAsync();
            return list;
        }

        public virtual async Task<TAggregateRoot> FindByIdAsync(string key, bool withCache = false)
        {
            var res = await Query.Where(x => x.Id == key).WithCacheIF(withCache).FirstAsync();

            return res;
        }

        public virtual async Task<TAggregateRoot> FirstOrDefaultAsync(Expression<Func<TAggregateRoot, bool>>? predicate = null, bool withCache = false)
        {
            var res = await Query.WhereIF(predicate is not null, predicate).WithCacheIF(withCache).FirstAsync();

            return res;
        }

        public virtual async Task<IPagedList<TAggregateRoot>> PagedFindAllAsync(Expression<Func<TAggregateRoot, object>> orderByKeySelector, OrderByMode orderByType, Pager pager, bool withCache = false)
        {
            RefAsync<int> totalCount = new RefAsync<int>();

            var page = await Query.OrderBy(orderByKeySelector, (orderByType != 0) ? OrderByType.Desc : OrderByType.Asc).ToPageListAsync(pager.PageIndex, pager.PageSize, totalCount);

            return new PagedList<TAggregateRoot>
            {
                PageIndex = pager.PageIndex,
                PageSize = pager.PageSize,
                TotalCount = totalCount.Value,
                Entities = page
            };
        }

        public virtual async Task<IPagedList<TAggregateRoot>> PagedFindAllAsync(Expression<Func<TAggregateRoot, bool>> predicate, Expression<Func<TAggregateRoot, object>> orderByKeySelector, OrderByMode orderByType, Pager pager, bool withCache = false)
        {
            RefAsync<int> totalCount = new RefAsync<int>();

            var page = await Query.Where(predicate).OrderBy(orderByKeySelector, (orderByType != 0) ? OrderByType.Desc : OrderByType.Asc).ToPageListAsync(pager.PageIndex, pager.PageSize, totalCount);


            return new PagedList<TAggregateRoot>
            {
                PageIndex = pager.PageIndex,
                PageSize = pager.PageSize,
                TotalCount = totalCount.Value,
                Entities = page
            };
        }


        public virtual async Task<IPagedList<TAggregateRoot>> PagedFindAllAsync(Expression<Func<TAggregateRoot, bool>> predicate, PageParam pager, bool withCache = false)
        {
            var page = await Query.Where(predicate).ToPagedOrderByListAsync(pager);

            return page;
        }

        public virtual async Task<int> RemoveAsync(TAggregateRoot entity)
        {
            return await DbContext.Deleteable(entity).ExecuteCommandAsync();
        }

        public virtual async Task<int> RemoveAsync(IEnumerable<TAggregateRoot> entities)
        {
            return await DbContext.Deleteable(entities.ToList()).ExecuteCommandAsync();
        }

        public virtual async Task<int> RemoveAsync(Expression<Func<TAggregateRoot, bool>> predicate)
        {
            return await DbContext.Deleteable<TAggregateRoot>().Where(predicate).ExecuteCommandAsync();
        }

        public virtual async Task<int> RemoveByIdAsync(string key)
        {
            return await DbContext.Deleteable<TAggregateRoot>().Where(x => x.Id == key).ExecuteCommandAsync();
        }

        public virtual async Task<int> SaveAsync(TAggregateRoot entity)
        {
            SetCreatedInfo(entity);
            SetUpdateInfo(entity);
            return await DbContext.Storageable(entity).ExecuteCommandAsync();
        }

        public virtual async Task<int> SaveAsync(IEnumerable<TAggregateRoot> entities)
        {
            foreach (var entity in entities)
            {
                if (entity is BaseOrgAuditableEntity auditable)
                {
                    if (auditable.CreatedTime is null)
                    {
                        SetCreatedInfo(entity);
                    }

                    if (auditable.CreatedTime is not null)
                    {
                        SetUpdateInfo(entity);
                    }
                }
            }
            return await DbContext.Storageable(entities.ToList()).ExecuteCommandAsync();
        }

        public virtual async Task<int> UpdateAsync(TAggregateRoot entity)
        {
            SetUpdateInfo(entity);
            return await DbContext.Updateable(entity).ExecuteCommandAsync();
        }

        public virtual async Task<int> UpdateAsync(IEnumerable<TAggregateRoot> entities)
        {
            foreach (var entity in entities)
            {
                SetUpdateInfo(entity);
            }
            return await DbContext.Updateable(entities.ToList()).ExecuteCommandAsync();
        }

        public async Task<int> UpdateColumnsAsync(TAggregateRoot entity, Expression<Func<TAggregateRoot, object>> columns)
        {
            SetUpdateInfo(entity);

            return await DbContext.Updateable(entity).UpdateColumns(columns).ExecuteCommandAsync();
        }

        public virtual async Task BeginTranAsync()
        {
            await DbContext.Ado.BeginTranAsync();
        }
        public virtual async Task CommitTranAsync()
        {
            await DbContext.Ado.CommitTranAsync();
        }
        public virtual async Task RollbackTranAsync()
        {
            await DbContext.Ado.RollbackTranAsync();
        }

        public async Task<int> SumAsync(Expression<Func<TAggregateRoot, int>> predicate)
        {
            return await Query.SumAsync(predicate);
        }


        private void SetCreatedInfo(TAggregateRoot entity)
        {
            if (entity is BaseOrgAuditableEntity auditable)
            {
                auditable.CreatedBy = $"{_orgContext.UserId}|{_orgContext.UserAccount}|{_orgContext.UserName}";
                auditable.CreatedTime = DateTime.Now;
            }
        }

        private void SetUpdateInfo(TAggregateRoot entity)
        {
            if (entity is BaseOrgAuditableEntity auditable)
            {
                auditable.UpdatedBy = $"{_orgContext.UserId}|{_orgContext.UserAccount}|{_orgContext.UserName}";
                auditable.UpdatedTime = DateTime.Now;
            }
        }

        public Task<int> BulkMergeAsync(List<TAggregateRoot> list)
        {
            return DbContext.Fastest<TAggregateRoot>().BulkMergeAsync(list);
        }
    }
}
